<?php

/**
 * @copyright ©2022 QuickAdmin
 * @author QinTingWei
 * @link http://www.quickadmin.cn/
 * Date Time: 2023/3/13
 */

namespace app\common\service\payment;


use app\common\model\PayOrder;
use app\common\model\PayOrderUnion;
use app\common\model\PayRefund;
use app\common\model\PayTypeConfig;
use app\common\service\CommonService;
use plugins\mall\service\payment\paymentRefund\PaymentRefund;
use plugins\mall\service\payment\paymentRefund\PaymentRefundInterface;
use quick\admin\Exception;
use think\facade\Log;

/**
 * Class Payment
 * @package app\common\service\payment
 */
class Payment extends CommonService
{

    /**
     * @var
     */
    public  $authCode;

    /**
     * @var
     */
    public string $plugin;


    /**
     * 创建收银订单
     * @param OrderDTO|OrderDTO[] $paymentOrderDTO
     * @param null $user
     * @return int 支付id
     * @throws \Exception
     */
    public function createPayOrder($paymentOrderDTO, $user = null): int
    {
        if (!is_array($paymentOrderDTO)) {
            $paymentOrderDTO = [$paymentOrderDTO];
        }
        if (!count($paymentOrderDTO)) {
            throw new \Exception("`$paymentOrderDTO`不能为空。");
        }

        foreach ($paymentOrderDTO as $item) {
            if (!($item instanceof OrderDTO)) {
                throw new \Exception("`$paymentOrderDTO`不是有效的OrderDTO对象。");
            }
        }

        $orderNos = [];
        $amount = 0;
        $title = '';
        foreach ($paymentOrderDTO as $order) {
            $orderNos[] = $order->orderNo;
            $amount = $amount + $order->amount;
            $title = $title . str_replace(';', '', $order->title) . ';';
        }
        $title = mb_substr($title, 0, 32);

        sort($orderNos);
        $orderNos[] = $amount;
        $unionOrderNo = 'HM' . mb_substr(md5(json_encode($orderNos)), 2);


        $orderUnion = new PayOrderUnion();
        $orderUnion->user_id = $user->id ?? 0;
        $orderUnion->pay_no = $unionOrderNo;
        $orderUnion->amount = $amount;
        $orderUnion->title = $title;
        $orderUnion->is_pay = 0;
        $orderUnion->pay_code = '';

        foreach ($paymentOrderDTO as $paymentOrder) {
            $supportPayTypes = $paymentOrder->supportPayTypes;
            if ($supportPayTypes) {
                $supportPayTypes = (array)$supportPayTypes;
            }
        }

        $orderUnion->support_pay_types = json_encode($supportPayTypes);
        $this->startTrans();
        try {
            if (!$orderUnion->save()) {
                throw new \Exception('支付失败,' . $orderUnion->getFirstError());
            }

            /** @var OrderDTO $paymentOrder */
            foreach ($paymentOrderDTO as $paymentOrder) {
                $model = new PayOrder();
                $model->order_union_id = $orderUnion->id;
                $model->order_no = $paymentOrder->orderNo;
                $model->plugin = $paymentOrder->plugin;
                $model->amount = $paymentOrder->amount;
                $model->title = $paymentOrder->title;
                $model->is_pay = 0;
                $model->pay_code = '';
                $model->notify_class = $paymentOrder->notifyClass;
                if (!$model->save()) {
                    throw new \Exception('支付失败');
                }
            }
            $this->commit();
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;
        }
        return $orderUnion->id;
    }



    /**
     * 获取支付服务
     * @param string $payType
     * @return PayTypeInterface
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function getPaymentService(string $payType)
    {
        $model = PayTypeConfig::where([
            'pay_code' => $payType,
            'status' => 1,
            'plugin' => $this->plugin,
            'code' => $this->authCode
        ])->find();
        if (empty($model)) {
            throw new \Exception('支付方式不支持。');
        }

        $options = json_decode($model->config,true);
        return PaymentTypeManager::instance()->getPayByCode($payType,$options);
    }


    /**
     * @param string $payType
     * @return string
     */
    public function getNotifyUrl(string $payType):string
    {
        $request = request();
        return $request->domain().$request->baseFile()."/admin/{$this->plugin}/{$payType}/{$this->authCode}";
    }



    /**
     * @param int $id
     * @param string $payType
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function payData(int $id, string $payType,array $data = [])
    {

        $payOrderUnion = PayOrderUnion::find(["id" => $id]);
        if (!$payOrderUnion) {
            throw new \Exception("待支付订单不存在");
        }

        if (intval($payOrderUnion->is_pay) === 1) {
            throw new \Exception('订单已支付。');
        }
        $payment = $this->getPaymentService($payType);

        $data['notifyUrl'] = $this->getNotifyUrl($payType);
        return $payment->payData($payOrderUnion,$data);
    }


    /**
     * @param string $payType 支付类型
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function notify(string $payType)
    {


        $payment = $this->getPaymentService($payType);
        try {
            $data = $payment->notify();
            if(empty($data['out_trade_no'])){
                // 有误
                return [];
            }
            $payOrderUnion = PayOrderUnion::find(["pay_no" => $data['out_trade_no']]);
            if($payOrderUnion || $payOrderUnion->is_pay){
                return [];
            }

            $payOrderUnion->is_pay = 1;
            $payOrderUnion->pay_code = $payType;
            $payOrderUnion->save();

            $orderList = PayOrder::where(['order_union_id' => $payOrderUnion->id])->select();
            foreach ($orderList as $order){

                $order->is_pay = 1;
                $order->pay_code = $payType;
                $order->save();

                $notifyClass = $order->notify_class;
                if (!class_exists($notifyClass) && !is_subclass_of($notifyClass,PayNotifyInterface::class)) {
                    continue;
                }


                try {
                    // 失败只记录日志不不能影响订单支付状态修改

                    /** @var PayNotifyInterface  $notify */
                    $notify = invoke($notifyClass);
                    $notify->notify($order);

                }catch (\Exception|\Throwable $e){
                    Log::error([
                        'msg' => $e->getMessage(),
                        'file' => $e->getFile(),
                        'line' => $e->getLine(),
                    ]);
                }

            }

        }catch (\Exception $e){
            Log::error([
                'msg' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
            ]);
        }



//        $payOrderUnion = PayOrderUnion::find(["id" => $id]);


//        return $payment->notifySuccess();
    }



    /**
     * @param string $orderNo
     * @param $price
     * @return int
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function refund(string $orderNo, $price): int
    {
        $payOrder = PayOrder::where([
            'order_no' => $orderNo,
            'is_pay' => 1,
        ])->find();
        if (!$payOrder) {
            throw new \Exception("订单未支付");
        }

        if (($payOrder->amount - $payOrder->refund) < $price) {
            throw new \Exception('退款金额大于可退款金额');
        }

        $orderUnion = PayOrderUnion::where(["id" => $payOrder->order_union_id])->find();

        $this->startTrans();
        try {
            $paymentRefund = new PayRefund();
            $paymentRefund->user_id = $orderUnion->user_id;
            $paymentRefund->amount = $price;
            $paymentRefund->order_no = "re" . md5($orderUnion->pay_no);
            $paymentRefund->is_pay = 0;
            $paymentRefund->pay_code = 0;
            $paymentRefund->title = "订单退款:{$orderNo}";
            $paymentRefund->out_trade_no = $orderUnion->pay_no;


            $paymentRefundService = $this->getRefundHandleClass($orderUnion->pay_type);
            if (!$paymentRefundService->refund($paymentRefund, $orderUnion)) {
                throw new \Exception("退款失败");
            }

            if (!$paymentRefund->save()) {
                throw new \Exception("退款失败:" . $paymentRefund->getErrorMsg());
            }

            $payOrder->refund += $price;
            if (!$payOrder->save()) {
                throw new \Exception("退款失败:" . $payOrder->getErrorMsg());
            }

            $this->commit();
        } catch (\Exception $e) {
            Log::error("订单退款失败:{$orderNo}  " . $e->getMessage());
            $this->rollback();
        }
        return $paymentRefund->id;

    }

    public function getRefundHandleClass(string $type): PaymentRefundInterface
    {
        $service = new PaymentRefund();
        return $service->getRefundService($type);
    }
}
